#ifndef DSPSIMULATOR_DSPINSTRUCTION_H
#define DSPSIMULATOR_DSPINSTRUCTION_H

#include <bitset>
#include <utility>
#include <unordered_set>
#include <functional>
#include <simulator/type/type.h>
#include <simulator/bitTools/bitTools.h>
#include <simulator/instruction/instruction.h>
#include <simulator/targetMachine/registerMap.h>
#include <dspSimulator/targetMachine/funcUnitInfo.h>
#include <dspSimulator/targetMachine/dspRegisterInfo.h>
#include <dspSimulator/targetMachine/dspMachine.h>
#include <dspSimulator/targetMachine/addressMode.h>
#include <dspSimulator/targetMachine/instructionInformat.h>
#include <dspSimulator/targetMachine/dspInstInfo.h>

class DSPSimulator;

class SPLoopBuffer;

class DspInstruction : public Instruction {
    friend class DSPSimulator;

    friend class SPLoopBuffer;

public:
    static u32 *src1LoRegPtr;
    static u32 *src1HiRegPtr;
    static u32 *src2LoRegPtr;
    static u32 *src2HiRegPtr;
    static u32 *dstLoRegPtr;
    static u32 *dstHiRegPtr;
    static u32 *cRegPtr;
    static u32 *cstPtr;
    static u32 *auxCstPtr;

    FuncUnit funcUnit{FuncUnit::UNKNOWN_FUNC};    // 函数单元类型

    InstFormat instFormat{InstFormat::Kind1};     // 指令格式，用于区分同一指令的同一函数单元的不同格式

    bool isCompactInstruction{false};       // 标识是否当前指令为Compact指令

    std::string compactInstKind;    // 当前compact指令的格式类型

    std::bitset<4> creg_z;           // standard instruction condition

    std::bitset<2> CC;              // compact instruction condition

    u32 numOfInsertNOP{0};  // 插入execute packet之后的nop指令的数量

    // 指令需要的属性
    u32 src1{0};             // source 1
    u32 src2{0};             // source 2
    u32 dst{0};              // destination
    u32 cst{0};              // constant
    u32 auxCst{0};           // auxiliary constant

    // compact指令需要的属性
    bool PROT{false};
    bool RS{false};
    std::bitset<3> DSZ;
    bool BR{false};
    bool SAT{false};

    // sploop执行所需要的属性
    u32 ii{0};
    u32 fstg{0};
    u32 fcyc{0};
    std::bitset<8> spMaskBitset;

    // load 和 store指令需要的属性
    u32 mode{0};             // addressing mode
    u32 offsetR{0};          // register offset
    u32 baseR{0};            // base address register

    std::bitset<5> crhi;     // crhi和crlo用于指定控制寄存器
    std::bitset<5> crlo;

    u32 s{0};                // side A or B for destination; 0 = side A, 1 = side B.
    u32 p{0};                // parallel execution; 0 = next instruction is not executed in parallel, 1 = next instruction is executed in parallel
    u32 y{0};                // .D1 or .D2 unit; 0 = .D1 unit, 1 = .D2 unit
    u32 x{0};                // cross path for src2; 0 = do not use cross path, 1 = use cross path
    u32 z{0};

public:
    /**
     * @param name 指令名
     * @param baseCycle 当前指令执行E1阶段的时钟周期
     * @param instructionAddress 指令地址
     * @param delay 延时大小
     */
    DspInstruction(const std::string &name, u32 address, u64 baseCycle, u64 delay) :
            Instruction(name, static_cast<u64>(address), baseCycle, delay) {};

    ~DspInstruction() override = default;

    DspInstruction(const DspInstruction &dspInstruction) = default;

    /**
     * 在执行ir函数前，需要更新寄存器和常量指针
     * @param read 用于读数据的寄存器对象
     * @param write 用于写数据的寄存器对象
     */
    void updateRegAndCstPtr(std::shared_ptr<RegisterMap> read, std::shared_ptr<RegisterMap> write);

    /**
     * 检查指令的执行条件
     * @param dspMachine dsq目标机
     * @return 如果指令的执行条件满足，返回true，否则返回false
     */
    virtual bool checkCondition(std::shared_ptr<DspMachine> dspMachine) = 0;

    virtual std::shared_ptr<DspInstruction> clone() = 0;

protected:

    /**
     * 检查Standard指令的执行条件
     * @param registerMap dsp处理器的寄存器组
     * @return 如果Standard指令的执行条件满足，返回true，否则返回false
     */
    bool checkStandardCondition(std::shared_ptr<DspMachine> dspMachine);

    /**
     * 判断Standard指令是否存在条件字段
     * @return 如果Standard指令存在条件字段，返回true，否则返回false
     */
    static bool existStandardCondition(std::bitset<32> data);

    /**
     * 获取Standard指令的条件字符串
     * @return Standard指令的条件字符串
     */
    std::string getStandardConditionString() const;

    /**
     * 获取指令在llvm jit中对应的函数名
    * @return 指令在llvm jit中对应的函数名
    */
    std::string getIRFuncName() const override;

    /**
     * 获取src1标识的32位寄存器名称
     * @return src1标识的32位寄存器名称
     */
    std::string getSrc1Reg32Name() const;

    /**
     * 获取src1标识的64位寄存器名称
     * @return src1标识的64位寄存器名称
     */
    std::string getSrc1Reg64Name() const;

    /**
     * 获取src2标识的32位寄存器名称
     * @return src2标识的32位寄存器名称
     */
    std::string getSrc2Reg32Name() const;

    /**
     * 获取src2标识的64位寄存器名称
     * @return src2标识的64位寄存器名称
     */
    std::string getSrc2Reg64Name() const;

    /**
     * 获取dst标识的32位寄存器名称
     * @return dst标识的32位寄存器名称
     */
    std::string getDstReg32Name() const;

    /**
     * 获取dst标识的64位寄存器名称
     * @return dst标识的64位寄存器名称
     */
    std::string getDstReg64Name() const;

    /**
     * 根据Crhi和CrLo字段获取控制寄存器的类型
     * @return 控制寄存器的类型
     */
    CRegKind getCRegKindByCrHiAndCrLo() const;

    /**
     * 根据BaseR字段获取基址寄存器名
     * @return 基址寄存器名
     */
    std::string getBaseRRegName() const;

    /**
     * 根据offsetR字段获取变指寄存器名
     * @return 变指寄存器名
     */
    std::string getOffsetRRegName() const;

    /**
     * 通过ir获取dstLoRegPtr指针的Value对象
     * @param llvmModule llvm模块
     * @param irBuilder llvm ir生成器
     * @return
     */
    static llvm::Value *getDstLoRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
    * 通过ir获取dstHiRegPtr指针的Value对象
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getDstHiRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
    * 通过ir获取src1LoRegPtr指针的Value对象
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getSrc1LoRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
    * 通过ir获取src1HiRegPtr指针的Value对象
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getSrc1HiRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
    * 通过ir获取src2LoRegPtr指针的Value对象
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getSrc2LoRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);


    /**
    * 通过ir获取src2HiRegPtr指针的Value对象
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getSrc2HiRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
    * 通过ir获取N比特大小的cst常数
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getCstN(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder, u32 N);


    /**
    * 通过ir获取N比特大小的auxCst常数
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getAuxCstN(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder, u32 N);

    /**
    * 通过ir获取cRegPtr指针的Value对象
    * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return
    */
    static llvm::Value *getCRegPtr(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
    * 根据寄存器寻址模式计算地址的实际偏移量
    * @param offset 输入的偏移量
    * @param mode 寄存器寻址模式
    */
    static void fixUpAddressOffset(u32 &offset, AddressingMode mode, std::shared_ptr<DspMachine> dspMachine);

    /**
    * 根据指令寻址模式计算地址的实际地址
    * @param baseRPtr 寻址的基址寄存器
    * @param mode  指令寻址的模式
    * @param leftshift 地址偏移量
    * @param dspMachine
    * @return 寻址访问内存的真实地址
    */
    u32 computeRealAddress(const u32 *baseRReadPtr, u32 *baseRWritePtr, u32 addressingMethod, u32 leftshift,
                           std::shared_ptr<DspMachine> dspMachine);

    /**
     * 取指包的RS位会影响Compact指令的寄存器的使用类型，通过该函数修正Compact指令，使寄存器表示为正式的数
     */
    void fixUpRegOnCompactInstruction();

    /**
    * 指令需要在执行前更新全局的状态信息，通过此函数实现该功能
    * @param targetMachine 目标机
    * @param llvmJit llvm jit执行引擎
    */
    void
    updateContextBeforeExecute(std::shared_ptr<TargetMachine> targetMachine, std::shared_ptr<LLVMJit> llvmJit) override;

    /**
     * 获取寻址指令的寻址模式字符串
     * @return 指令的寻址模式字符串
     */
    std::string addressModeToString() const;

    /**
     *
     * @param llvmModule llvm模块
     * @param irBuilder llvm ir生成器
     * @return 40位src1标识的寄存器数据
     */
    static llvm::Value *loadLongSrc1ByIR(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
     * @param llvmModule llvm模块
    * @param irBuilder llvm ir生成器
    * @return 40位src2标识的寄存器数据
    */
    static llvm::Value *loadLongSrc2ByIR(llvm::Module *llvmModule, std::shared_ptr<llvm::IRBuilder<>> irBuilder);

    /**
     * @param irBuilder llvm ir生成器
     * @param longSrc 源40位数据
     * @param dstLoPtr 存储40位数据中的低32位数据的目的寄存器
     * @param dstHiPtr 存储40位数据中的高8位数据的目的寄存器
     */
    static void storeLongIntoDst(std::shared_ptr<llvm::IRBuilder<>> &irBuilder, llvm::Value *longSrc,
                                 llvm::Value *dstLoPtr, llvm::Value *dstHiPtr);

};

#endif //DSPSIMULATOR_DSPINSTRUCTION_H
